package controller;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;

import javax.swing.JOptionPane;

import model.CFCQuery;
import model.DocSimilar;
import model.InvIndex;
import model.ModeloBooleano;
import model.ModeloVetorial;
import model.RepVetorial;
import util.CFC;
import util.Constants;
import util.Read;
import view.FileChooser;

/**
 * The Main class has the application start-up stuff.
 * 
 * @author Diogo Kiss and Valerio Lopes
 */
public class Main
{
	private static final long serialVersionUID = 1L;
	private static String directory;
	private static InvIndex index = new InvIndex();
	private static RepVetorial dv;
	private static char algoritmo;
	private static boolean cfcdb = false;
	private static CFC cfc;

	/**
	 * The main method starts up the application, invoking the proper objects.
	 */

	public static void main(String[] args) 
	{		
		boolean quit = false;

		//********************************
		cfc = new CFC();
		//cfc.parseCFCData();
		//directory = cfc.getPath();
		cfcStuff();
		//********************************
		System.exit(0);
		
		//CFC files or ordinary files?
		System.out.println("Would you like to use the CFC database? [Y/N]");
		
		try
		{
			while(!quit)
			{
				byte[] response = new byte[1];
				System.in.read(response,0,1);
				char answer = (new String(response)).charAt(0);
				
				if( (answer == 'Y') || (answer == 'y') )
				{
					cfcdb = true;
					quit = true;
					//Parse CFC data, generating files to be indexed
					cfc = new CFC();
					cfc.parseCFCData();
					directory = cfc.getPath();					
				}
				else if( (answer == 'N') || (answer == 'n') )
				{
					quit = true;
				}
				
				if(!quit)
				{
					System.out.println("Invalid answer! Type 'Y' for yes or 'N' for no.\n");
					System.out.println("Would you like to use the CFC database? [Y/N]");	
				}
				
			}
			
		} catch (IOException e1)
		{
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
		
		if(!cfcdb) //If CFC will not be used, another database is needed
		{
			// Asking for the files database
			System.out.println("Please, locate the files database directory you want to use:");
			FileChooser fc = new FileChooser();		
			
			// Validate directory
			directory = fc.getDBDirectory();
			
			if( directory == null)
			{
				JOptionPane.showMessageDialog (null,"Invalid directory!", "Invalid directory",JOptionPane.ERROR_MESSAGE);
				System.exit(0);
			}
			else if(directory.equals("cancel"))
			{
				JOptionPane.showMessageDialog (null,"Operation aborted!", "Aborted",JOptionPane.INFORMATION_MESSAGE);
				System.exit(0);
			}
		}

		// Pre-processing files
		if(false) preProcessing();
					
		boolean menu = true;
		
		// Choosing query implementation
		
		while(menu)
		{
		
			System.out.println("\nChoose an algorithm:");
			System.out.println("[S] " + Constants.ALGORITMO_SIMPLES);
			System.out.println("[B] " + Constants.ALGORITMO_BOOLEANO);
			System.out.println("[V] " + Constants.ALGORITMO_VETORIAL);
			System.out.println("or [C] to analise CFC data.\n");
			System.out.println("or [Q] to quit this program.\n");
			
			byte[] option = new byte[1];
		
			try
			{
				System.in.read(option,0,1);
				algoritmo = (new String(option)).charAt(0);
				
				if(algoritmo == 'Q')
				{
					menu = false;
				}
				else if( !(algoritmo == 'S') && !(algoritmo == 'B') && !(algoritmo == 'V') && !(algoritmo == 'C'))
					System.out.println("Invalid option! Try again...\n");
				else
				{
					switch(algoritmo)
					{
						case 'S':
							simpleSearch();
						break;
							
						case 'B':
							booleanSearch();
						break;
						
						case 'V':
							vetorialSearch();
						break;
						
						case 'C':
							cfcStuff();
						break;
							
					}
				}
				
			} catch (IOException e)
			{
				// TODO Auto-generated catch block
				System.out.println("Error while reading implementantion option!\n");
			}
		
		}
		
		System.out.println("Bye!");
		System.exit(0);
	}

	private static void preProcessing()
	{

		Read leitor = new Read();
		System.out.println("Building inverted index...");
		leitor.scan(new File(directory),index,true);

		dv = new RepVetorial(index);
		System.out.println("Building vectorial representation...");
		dv.create();

	}
	
	private static void simpleSearch()
	{
		String query = JOptionPane.showInputDialog("[BUSCA SIMPLES] Digite a palavra a ser buscada:");
		
		if(query == null || "".equals(query))
		{
			JOptionPane.showMessageDialog (null,"Busca cancelada!", "Cancelado",JOptionPane.INFORMATION_MESSAGE);
			return;
		}
		
		System.out.println("Buscando por '" + query + "'...");
		
		//Preparing the String
		query.trim().toLowerCase().replaceAll("\t", "");
		
		String results = index.search(query.toLowerCase(),true);
		System.out.println("------------------------------------");
		System.out.println("Resultado: \n" + results + "\n");
		System.out.println("------------------------------------");
		
	}
	
	private static void booleanSearch()
	{
		String entrada = JOptionPane.showInputDialog("[BUSCA BOOLEANA] Digite a query para busca booleana (vazio para cancelar):");

		if(entrada == null || "".equals(entrada))
		{
			JOptionPane.showMessageDialog (null,"Busca abortada!", "INFO",JOptionPane.INFORMATION_MESSAGE);
			return;				
		}
		
		System.out.println("=========================");
		System.out.println("QUERY BOOLEANA: " + entrada);

		ModeloBooleano mb = new ModeloBooleano(index);
		ArrayList<String> resposta = mb.buscaBooleana(entrada.toLowerCase(),true);
		
		
		if(resposta.size() > 0)
		{
			for(String file : resposta)
				System.out.println(file);
		}
		else
			System.out.println("Nenhum documento corresponde a consulta informada :-(");

	}
	
	private static void vetorialSearch()
	{
		// Flags de operadores AND, OR e BUT
		Vector<String> termosNegados = new Vector<String>(); // Armazenar termos negados do operador BUT		
		ModeloVetorial modVet = new ModeloVetorial(index);
		
		System.out.println("Criando as estruturas do modelo vetorial...");
		
		modVet.criarVetoresDocs();
		
		String queryVetorial = JOptionPane.showInputDialog("[BUSCA VETORIAL] Digite a query para busca vetorial (vazio para cancelar):\n\nINSTRU��ES:\n\n- Para usar AND, digite AND(consulta);\n- Para o uso do OR, digite OR(consulta)\n- Consultas com BUT devem ser expressas na forma BUT(t1 -t2 t3 t4 -t5 ..... etc)\n\nPara utilizar o modelo vetorial sem os operadores, simplesmente digite a consulta");

		boolean flagAnd = false, flagOr = false, flagBut = false;
		if(queryVetorial == null || "".equals(queryVetorial))
		{
			JOptionPane.showMessageDialog (null,"Busca abortada!", "INFO",JOptionPane.INFORMATION_MESSAGE);
			return;
		}
		// Processamento da consulta
		// HashTable com par chave x valor
		// Chave = palavra; valor = numero de vezes que aparece na consulta
		// Sera util na hora do calculo do vetor consulta
		String queryOriginal = queryVetorial;
		if(queryVetorial.startsWith("AND"))
		{
			flagAnd = true;
			queryVetorial = queryVetorial.substring(4, queryVetorial.length()-1);
		}
		else 
		{
			
			if(queryVetorial.startsWith("OR"))
			{
				flagOr = true;
				queryVetorial = queryVetorial.substring(3, queryVetorial.length()-1);
				
			}
			else if(queryVetorial.startsWith("BUT"))
			{

				flagBut = true;
				queryVetorial = queryVetorial.substring(4, queryVetorial.length()-1);			
				
				// Agora vamos remover os termos negados
				String[] termosConsulta = queryVetorial.split(" ");					
										
				String novaQuery = "";
				
				for(int i=0;i < termosConsulta.length; i++)
				{
					if(termosConsulta[i].charAt(0) != '-')
					{
						novaQuery += termosConsulta[i] + " "; // nova
																// query
																// contem
																// apenas os
																// termos
																// n�o-negados
					}
					else
					{
						termosNegados.add(termosConsulta[i].substring(1, termosConsulta[i].length())); // lista
																										// de
																										// termos
																										// negados,
																										// para
																										// posterior
																										// filtragem
					}
				}
				// A query agora n�o possui os termos negados
				// Est�o armazenados agora no vetor termosNegados
				queryVetorial = novaQuery.trim();
				
			}					
		
		}
						
		System.out.println("=========================");
		System.out.println("QUERY VETORIAL: " + queryOriginal);
		
		
		
		if(flagOr)
		{ // CONSULTA OU
			Hashtable<String,DocSimilar> respostaFinal = new Hashtable<String,DocSimilar>(); // Uni�o
																								// das
																								// respostas
			String[] termos = queryVetorial.split(" ");
			
			for(String t : termos)
			{
				ArrayList<DocSimilar> respTemp = modVet.consulta(t.toLowerCase(),true);
				
				// varro a resposta da consulta do termo unico
				for(DocSimilar d : respTemp)
				{
					if(respostaFinal.get(d.getArquivo()) != null)
					{
						if(respostaFinal.get(d.getArquivo()).getSimilaridade() < d.getSimilaridade())
						{
							respostaFinal.put(d.getArquivo(),d);
						}
					}
					else
						respostaFinal.put(d.getArquivo(),d);
				}
				
			}

			// HashTable pronta
			// Varremos ela agora pra obter o ArrayList de DocSimilar's e
			// ordena-lo finalmente
			Enumeration<String> rTemp = respostaFinal.keys();
			ArrayList<DocSimilar> respFinal = new ArrayList<DocSimilar>();
			while(rTemp.hasMoreElements())
			{
				respFinal.add(respostaFinal.get(rTemp.nextElement()));
			}
			
			Collections.sort(respFinal);
			for(DocSimilar d : respFinal)
			{
				System.out.println(d.getArquivo()+" - Similaridade: "+d.getSimilaridade());
			}
		
		}
		else
		{ // AND, BUT ou Normal
			ArrayList<DocSimilar> resposta = modVet.consulta(queryVetorial.toLowerCase(),true);
			
			if(resposta.size() > 0)
			{
				for(DocSimilar item : resposta)
				{
					if(flagBut) 
					{ // Operador BUT: varremos o vetor de termos negados
						// e excluimos os documentos retornados que os
						// cont�m
						boolean exibe = true;
						for(String s : termosNegados)
						{
							if(index.contains(s, item.getArquivo()))
								exibe = false;
						}
						if(exibe)
							System.out.println(item.getArquivo()+" - Similaridade: "+item.getSimilaridade());
					}
					else
					{ 
						if(flagAnd)
						{// AND
						 // Pra cada DOC retornado, verificar se todas as
							// palavras da consulta est�o presentes...
							String[] termosConsulta = queryVetorial.toLowerCase().split(" ");
							boolean exibe = true;
							for(String s : termosConsulta)
							{
								if(!index.contains(s, item.getArquivo()))
								{
									exibe = false;
									break;
								}
							}								
							if(exibe)
								System.out.println(item.getArquivo()+" - Similaridade: "+item.getSimilaridade());
						}
						else // Normal..
							System.out.println(item.getArquivo()+" - Similaridade: "+item.getSimilaridade());								
					}
				}
			}
			else
				System.out.println("Nenhum documento corresponde a consulta informada :-(");
		}
		
		queryVetorial = JOptionPane.showInputDialog("[BUSCA VETORIAL] Digite a query para busca vetorial (vazio para cancelar):\n\nINSTRU��ES:\n\n- Para usar AND, digite AND(consulta);\n- Para o uso do OR, digite OR(consulta)\n- Consultas com BUT devem ser expressas na forma BUT(t1 -t2 t3 t4 -t5 ..... etc)\n\nPara utilizar o modelo vetorial sem os operadores, simplesmente digite a consulta");

	}
	
	private static void cfcStuff()
	{
		System.out.println("Processing Query File...");
		Vector<CFCQuery> queries = cfc.parseQueryFile();
		
		System.out.println("Total of queries read: " + queries.size() );
		
		System.out.println("For Rew, these docs are relevant with query 2:");
		for(String s : cfc.getRD(2,1))
		{
			System.out.println("Documento: " + s);
		}
				
	}
	
}